package haw.pr2.A2a;

import haw.pr2.A2a.JGameAdapter;
import haw.pr2.A2a.ParticleInterface;
import jgame.JGObject;

/** Aufgabe 1
 */
public class Aufgabe1b {

    public static void main(String[] args) {
        Aufgabe1b game = new Aufgabe1b();
        game.initGame();
    }

    private void initGame() {
        JGameAdapter.registerPartikle(new Porsche911GT2RS());
        JGameAdapter.startEngine(800, 600);
    }

    /** Our user-defined object. */
    class Porsche911GT2RS implements ParticleInterface {
        //Konstanten
        private final double NEUTRAL = 0.0;
        private final double EIS = 0.1;
        private final double SCHNEE = 0.3;
        private final double NAESSE = 0.7;
        private final double NORMAL = 1.0;
        private final double KW = 456.0;
        private final double MAX_SPEEED = 330.0;
        private final double MIN_SPEED = 0.0000000000001;
        private final double KILO = 1000.0;
        private final double HOUR_IN_SEC = 3600.0;
        private final double mass = 1445.0; // [kg]
        private final double KMH_IN_MS = KILO / HOUR_IN_SEC;
        private final double powerPropMax = KW * KILO; // [ ??] [W]
        private final double speedMax = MAX_SPEEED * KILO / HOUR_IN_SEC; //km/h * 1000 /3600 = [ms^-1]
        private final double dragConst = Math.abs(powerPropMax / (Math.pow(speedMax, 3))); // W/(m/s)^3
        private final double accEarth = 9.81; // m/s^2
        private double underground=NORMAL;
        private double forcePropMax = mass * accEarth * underground; //KG * m/s^2 * koeffizenten
        private final double YPos = 200.0; //Y-Position des Autos
        //Eigenschaften von Porsche
        private double speed = NEUTRAL; // m/s
        private double level = NEUTRAL;
        private double pos = NEUTRAL; //Startposition
        private boolean abs = true;
        private boolean asr = true;
        private boolean baem = false;
        //Eigenschaften von Umwelt
        private double time = NEUTRAL; //vergangene Zeit in Sekunden
        private double acc = NEUTRAL; // in m/s^2

        /** Constructor. */
        Porsche911GT2RS() {
            reset();
        }

        private void reset() {
            this.baem = false;
            set(NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL);
        }

        /*
         * Setzt die aktuellen Werte
         */
        private void set(double _time, double _pos, double _speed, double _level) {
            double force = NEUTRAL;
            double forceProp = NEUTRAL;
            double forceDrag = NEUTRAL;
            double powerProp = NEUTRAL;
            double forcePropAbs = NEUTRAL;
            double forceDir = NEUTRAL;
            final boolean fbreak = _level < NEUTRAL;
            // Set globals
            this.speed = _speed; // m/s
            this.level = _level; // -1.0..+1.00
            this.pos = _pos; // X-Position
            this.time = _time; // in Sekunden

            powerProp = Math.abs(this.level) * this.powerPropMax; // -1.0..+1.0 * W
            
            /** neue Berechnung
             * Wenn die Bremse betaetigt wird, dann wird die Bremskraft auf 
             * maximal 1G gesetzt, sonst ist die Kraft vorwaehrts gerichtet und 
             * entspricht der alten Berechnung
             */
            forceDir=fbreak ? mass * accEarth * Math.abs(this.level) : (powerProp / Math.abs(this.speed));
 
            /* ABS & ASR
             * Ist ABS aktiviert und es wird gebremst, oder ASR aktiviert und es wird nicht gebremst
             * So wird kontrolliert beschleunigt bzw. gebremst.
             */
            if ((abs && fbreak) || (asr && !fbreak) ){
                forcePropAbs = Math.min(this.forcePropMax, forceDir);
                
            /* Ist die Bremskraft groesser als der maximal zu uebertragenen Kraft
             * auf die Strasse, so wird ueberbremst! 
             */
            }else if ((forceDir > this.forcePropMax) && fbreak) {
                this.baem = true;
                this.speed=NEUTRAL;
            /* Ist die Beschleunigung groesser als die maximal zu uebertragenen
             * Kraft auf die Strasse, so wird ueberbremst! 
             */
            }else if ((forceDir > this.forcePropMax) && !fbreak){
                this.baem = true;
                //this.level=NEUTRAL;
                this.speed=NEUTRAL;
            // Wenn alles OKAY! ist, dann entspricht die Beschleunigungskraft
            // bzw. Bremskraft gleich der zu uebertragenen Kraft.
            }else {
                forcePropAbs=forceDir;
            }
                
            forcePropAbs = Math.min(this.forcePropMax, forceDir); // minimum( W ,  W / m/s )  
            
            forceProp = forcePropAbs * Math.signum(this.level); // minimum( W , W / (m/s) )
            forceDrag = this.dragConst * (Math.pow(this.speed, 2) * Math.signum(-this.speed)); // W / (m/s)^3 * ( (m/s)^2 * m/s ) = W
            force = forceProp + forceDrag; //  minimum ( W , W / (m/s))
            this.acc = force / this.mass; // minimum ( W / KG , m/s )
        }

        /*
         * Führt einen Simulationsschritt aus
         */
        @Override
        public void step(double deltaTime, double _level) {
            double ltime; // in Sekunden
            double lpos; // X-Position
            double lspeed; // m/s

            ltime = this.time + deltaTime; // s

            lpos = this.pos + (this.speed * deltaTime); // m

            //Auto braucht eine Anfangsgeschwindigkeit um anfahren zu können
            lspeed = (this.speed <= NEUTRAL) ? MIN_SPEED : this.speed + (this.acc * deltaTime); // m/s
            
            //Testausgabe
//        System.out.printf("Speed:" + lspeed * 3.6); //Testausgabe
//        System.out.println("\t\ttime:" + ltime); //Testausgabe
//        System.out.println("\t\tpos:" + lpos); //Testausgabe

            //Auto zurücksetzen wenn es am Ende angekommen ist
            if (lpos > 250) {
                lpos = 0;
            }

            set(ltime, lpos, lspeed, _level);
        }
        
        
        
        @Override
        public double getXInMeters() {
            return this.pos;
        }

        @Override
        public double getYInMeters() {
            return this.YPos;
        }

        @Override
        public double getLevel() {
            return this.level;
        }

        @Override
        public double getSpeed() {
            return Math.round(this.speed * 3.6 * 100d) / 100d; // km/h
        }

        @Override
        public double getTime() {
            return Math.round(this.time * 100d) / 100d; //sek
        }

        @Override
        public void toggleAbs() {
            abs= (!abs);
        }

        @Override
        public void toggleAsr() {
            asr= (!asr);
        }

        @Override
        public boolean getAbsState() {
            return abs;
        }

        @Override
        public boolean getAsrState() {
            return asr;
        }

        @Override
        public boolean getExplState() {
            return baem;
        }

        @Override
        public void restGame() {
            reset();
        }

        @Override
        public void toggleUnderground() {
            if(this.underground == NORMAL) {
                this.underground = NAESSE;
                this.forcePropMax = mass * accEarth * this.underground;
            } else if (this.underground == NAESSE) {
                this.underground = SCHNEE;
                this.forcePropMax = mass * accEarth * this.underground;
            } else if (this.underground == SCHNEE){
                this.underground = EIS;
                this.forcePropMax = mass * accEarth * this.underground;
            } else {
                this.underground = NORMAL;
                this.forcePropMax = mass * accEarth * this.underground;
            }
                    
        }

        @Override
        public String getUnderground() {
            if(this.underground == NORMAL) {
                return "Normal";
            } else if (this.underground == NAESSE) {
                return "Naesse";
            } else if (this.underground == SCHNEE){
                return "Schnee";
            } else if (this.underground == EIS) {
                return "Eis";
            } else {
                return "Unknown Underground";
            }
        }
        
    } /* end class Porsche911GT2RS */

}
